package vis;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.Vector;

import javax.swing.JPanel;

public class Viewport extends JPanel implements MouseMotionListener, MouseListener, MouseWheelListener {
	private static final long serialVersionUID = 6189654807530892166L;
	
	private Scene scene = null;
	private double offX = 0.0;
	private double offY = 0.0;
	private double scale = 1.0;
	private boolean drag = false;
	private double lastX = 0.0;
	private double lastY = 0.0;
	private double lastOX = 0.0;
	private double lastOY = 0.0;
	
	private double sweepX = 0.0;
	
	private Vector<MouseListener> listeners = new Vector<MouseListener>();
	
	public void addVPListener(MouseListener ml)
	{
		listeners.add(ml);
	}
	
	public void removeVPListener(MouseListener ml)
	{
		listeners.remove(ml);
	}
	
	public void setSweepX(double x)
	{
		sweepX = x;
		repaint();
	}
	
	public double getSweepX()
	{
		return sweepX;
	}
	
	public Viewport(Scene s)
	{
		super(true);
		scene = s;
		this.addMouseMotionListener(this);
		this.addMouseListener(this);
		this.addMouseWheelListener(this);
	}
	
	private static final Color gridCol = new Color(0xF0F0F0);
	private static final Stroke sweepStroke = new BasicStroke(3.0f);
	
	private void drawGrid(Graphics2D g)
	{
		int hW = getWidth() / 2;
		int hH = getHeight() / 2;
		double nX = (offX - hW) + hW;
		double nY = (offY - hH) + hH;
		
		g.setColor(gridCol);
		
		int gran = 20;
		if(scale < 1.0)
			gran = 40;
		if(scale > 2.0)
			gran = 10;
		
		int startX = (int)(-offX / scale);
		startX -= startX % gran;
		int startY = (int)(-offY / scale);
		startY -= startY % gran;
		
		int lenX = (int)(getWidth() / scale) + gran;
		int lenY = (int)(getHeight() / scale) + gran;
		
		for(int x = startX; x <= startX + lenX; x += gran)
		{
			g.drawLine((int)(x * scale + offX), 0, (int)(x * scale + offX), getHeight());
		}
		for(int y = startY; y <= startY + lenY; y += gran)
		{
			g.drawLine(0, (int)(y * scale + offY), getWidth(), (int)(y * scale + offY));
		}
		
		g.setColor(Color.CYAN);
		g.drawLine(0, (int)nY, getWidth(), (int)nY);
		g.drawLine((int)nX, 0, (int)nX, getHeight());
		
		if(sweepX != 0.0)
		{
			Stroke s = g.getStroke();
			g.setColor(Color.yellow);
			startX = (int) (sweepX + (offX));
			g.setStroke(sweepStroke);
			g.drawLine((int)startX, 0, (int)startX, getHeight());
			g.setStroke(s);
		}
	}
	
	public void paint(Graphics gg)
	{
		Graphics2D g = (Graphics2D)gg;
		g.setBackground(Color.white);
		g.clearRect(0, 0, getWidth(), getHeight());
		
		drawGrid(g);
		
		g.translate(offX, offY);
		g.scale(scale, -scale);
		
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		if(scene != null)
			scene.render(g);
	}
	
	public void setOffset(double X, double Y)
	{
		offX = X;
		offY = Y;
		repaint();
	}
	
	public void offset(double X, double Y)
	{
		offX += X;
		offY += Y;
		repaint();
	}
	
	public void setScale(double s)
	{
		scale = s;
		repaint();
	}
	
	public void scale(double s)
	{
		scale *= s;
		if(scale < 0.4) scale = 0.4;
		if(scale > 4.0) scale = 4.0;
		repaint();
	}

	@Override
	public void mouseDragged(MouseEvent arg0) {
		if(drag)
		{
			double nX = arg0.getX();
			double nY = arg0.getY();
			setOffset(lastOX + (nX - lastX), lastOY + (nY - lastY));
		}
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {}

	@Override
	public void mouseClicked(MouseEvent arg0) {}

	@Override
	public void mouseEntered(MouseEvent arg0) {}

	@Override
	public void mouseExited(MouseEvent arg0) {}

	@Override
	public void mousePressed(MouseEvent arg0) {
		if(arg0.getButton() == MouseEvent.BUTTON3)
		{
			drag = true;
			lastX = arg0.getX();
			lastY = arg0.getY();
			lastOX = offX;
			lastOY = offY;
		}
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		if(arg0.getButton() == MouseEvent.BUTTON1)
		{
			int x = (int) (arg0.getX() - offX);
			int y = (int) (arg0.getY() - offY);
			MouseEvent mev = new MouseEvent(this, 0, arg0.getWhen(), 0, x,-y, 0, 0, 1, false, MouseEvent.BUTTON1);
			for(MouseListener l : listeners)
				l.mouseReleased(mev);
		}
		
		if(arg0.getButton() == MouseEvent.BUTTON3)
		{
			drag = false;
			//System.out.println(offX);
			//System.out.println(offY);
		}
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent arg0) {
		//double ns = Math.pow(1.1, -arg0.getWheelRotation());
		//scale(ns);
	}

}
